#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include "../include/list.h"
#include "../include/set.h"

void set_init(set_t *set, int (*match)(const void *key1, const void *key2), void (*destroy)(void *data))
{
	list_init(set, destroy);
	set->match = match;

	return;
}

int set_insert(set_t *set, const void *data)
{
	if (set_is_member(set, data))
		return 1;

	return list_insert_next(set, list_tail(set), data);
}


int set_remove(set_t *set, void **data)
{
	list_node_t *prev, *cur;
	prev = NULL;

	for (cur = list_head(set); cur != NULL; cur = list_next(cur))
	{
		if (set->match(*data, list_data(cur)))
			break;
		prev = cur;
	}

	if (!cur)
		return -1;

	return list_remove_next(set, prev, data);
}


int set_union(set_t * setu, const set_t * set1, const set_t * set2)
{
	list_node_t *n;
	void *data;

	set_init(setu, set1->match, NULL);

	for (n = list_head(set1); n != NULL; n = list_next(n))
	{
		data = list_data(n);
		if (list_insert_next(setu, list_tail(setu), data)){
			set_destroy(setu);
			return -1;
		}
	}

	for (n = list_head(set2); n != NULL; n = list_next(n))
	{
		data = list_data(n);
		if (set_is_member(setu, data))
			continue;
		else{
			if (list_insert_next(setu, list_tail(setu), data)){
				set_destroy(setu);
				return -1;
			}
		}
	}

	return 0;
}


int set_intersection(set_t * seti, const set_t * set1, const set_t * set2)
{
	list_node_t *n;
	void *data;

	set_init(seti, set1->match, NULL);
	for (n = list_head(set1); n != NULL; n = list_next(n))
	{
		if (set_is_member(set2, list_data(n))){
			data = list_data(n);
			if (list_insert_next(seti, list_tail(seti), data)){
				set_destroy(seti);
				return -1;
			}
		}
	}
}


int set_difference(set_t * setd, const set_t * set1, const set_t * set2)
{
	list_node_t *n;
	void *data;

	set_init(setd, set1->match, NULL);

	for (n = list_head(set1); n != NULL; n = list_next(n))
	{
		if (!set_is_member(set2, list_data(n))){
			data = list_data(n);
			if (list_insert_next(setd, list_tail(setd), data)){
				set_destroy(setd);
				return -1;
			}
		}
	}

	return 0;
}


int set_is_member(const set_t * set, const void * data)
{
	list_node_t *n;

	for (n = list_head(set); n != NULL; n = list_next(n))
	{
		if (set->match(data, list_data(n)))
			return 1;
	}

	return 0;
}


int set_is_subset(const set_t * set1, const set_t * set2)
{
	list_node_t *n;

	if (set1->size > set2->size)
		return 0;

	for (n = list_head(set1); n != NULL; n = list_next(n))
	{
		if (!set_is_member(set2, list_data(n)))
			return 0;
	}

	return 1;
}


int set_is_equal(const set_t * set1, const set_t * set2)
{
	list_node_t *n;

	if (set1->size != set2->size)
		return 0;

	return set_is_subset(set1, set2);
}

